package thesis.filesharing.dao.impl;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.stereotype.Repository;

import thesis.filesharing.common.exception.system.AppSystemException;
import thesis.filesharing.common.valueobjects.SearchLecturerValueObject;
import thesis.filesharing.dao.LecturerDAO;
import thesis.filesharing.dao.util.EMF;
import thesis.filesharing.model.Lecturer;

import com.google.appengine.api.datastore.Key;

@Repository
public class LecturerDAOImpl implements LecturerDAO {
	
	@SuppressWarnings("unchecked")
	@Override
	public Lecturer findLecturerById(String lecturerId) {
		EntityManager em = null;
		
		try {
			em = EMF.get().createEntityManager();
			String jpaql = "SELECT l FROM Lecturer l WHERE l.lecturerId = :lecturerId";
			Query query = em.createQuery(jpaql);
			query.setParameter("lecturerId", lecturerId);
			List<Lecturer> lecturers = (List<Lecturer>) query.getResultList();
			if (lecturers != null && !lecturers.isEmpty()) {
				return lecturers.get(0);
			}
			return null;
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new AppSystemException(ex);
		} finally {
			em.close();
		}
	}

	@Override
	public Lecturer saveLecturer(Lecturer lecturer) {
		
		EntityManager em = null;
		EntityTransaction transaction = null;
		
		try {
			em = EMF.get().createEntityManager();
			transaction = em.getTransaction();
			transaction.begin();
			
			em.persist(lecturer);
			em.flush();
			
			transaction.commit();
			return lecturer;
		} catch (Exception ex) {
			if (transaction != null && transaction.isActive()) {
				transaction.rollback();
			}
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public Lecturer findLecturerByEmail(String lecturerEmail) {
		EntityManager em = null;
		try {
			em = EMF.get().createEntityManager();
			String jpaql = "SELECT l FROM Lecturer l WHERE l.lecturerEmail = :lecturerEmail";
			Query query = em.createQuery(jpaql);
			query.setParameter("lecturerEmail", lecturerEmail);
			List<Lecturer> lecturers = (List<Lecturer>) query.getResultList();
			if (lecturers != null && !lecturers.isEmpty()) {
				return lecturers.get(0);
			}
			return null;
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Lecturer> listLecturers(int page, int pageSize) {
		EntityManager em = null;
		try {
			em = EMF.get().createEntityManager();
			String jpaql = "SELECT l FROM Lecturer l ORDER BY l.lecturerId";
			Query query = em.createQuery(jpaql);
			query.setFirstResult((page - 1) * pageSize);
			query.setMaxResults(pageSize);
			return (List<Lecturer>) query.getResultList();
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}

	@Override
	public int countLecturers() {
		EntityManager em = null;
		try {
			em = EMF.get().createEntityManager();
			String jpaql = "SELECT l FROM Lecturer l";
			Query query = em.createQuery(jpaql);
			return query.getResultList().size();
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}

	@Override
	public void deleteLecturer(Key lecturerKey) {
		EntityManager em = null;
		EntityTransaction transaction = null;
		try {
			em = EMF.get().createEntityManager();
			transaction = em.getTransaction();
			transaction.begin();
			Lecturer lecturer = em.find(Lecturer.class, lecturerKey);
			if (lecturer != null) {
				em.remove(lecturer);
			}
			transaction.commit();
		} catch (Exception ex) {
			if (transaction != null && transaction.isActive()) {
				transaction.rollback();
			}
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}

	@Override
	public Lecturer findLecturerByKey(Key lecturerKey) {
		EntityManager em = null;
		try {
			em = EMF.get().createEntityManager();
			return em.find(Lecturer.class, lecturerKey);
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}

	@Override
	public Lecturer updateLecturer(Lecturer lecturer) {
		EntityManager em = null;
		EntityTransaction transaction = null;
		try {
			em = EMF.get().createEntityManager();
			transaction = em.getTransaction();
			transaction.begin();
			Lecturer l = em.find(Lecturer.class, lecturer.getLecturerKey());
			if (l != null) {
				l.setLecturerId(lecturer.getLecturerId());
				l.setLecturerName(lecturer.getLecturerName());
				l.setLecturerEmail(lecturer.getLecturerEmail());
				l.setLecturerDepartment(lecturer.getLecturerDepartment());
				l.setLecturerPassword(lecturer.getLecturerPassword());
				l.setRoles(lecturer.getRoles());
				em.merge(l);
				em.flush();
			}
			transaction.commit();
			return l;
		} catch (Exception ex) {
			if (transaction != null && transaction.isActive()) {
				transaction.rollback();
			}
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean isEmailDuplicatedWithOrtherLecturer(String lecturerEmail, Key lecturerKey) {
		EntityManager em = null;
		try {
			em = EMF.get().createEntityManager();
			String jpaql = "SELECT l FROM Lecturer l WHERE l.lecturerKey <> :lecturerKey AND l.lecturerEmail = :lecturerEmail";
			Query query = em.createQuery(jpaql);
			query.setParameter("lecturerEmail", lecturerEmail);
			query.setParameter("lecturerKey", lecturerKey);
			List<Lecturer> lecturers = (List<Lecturer>) query.getResultList();
			return lecturers != null && !lecturers.isEmpty();
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}

	@Override
	public int countFoundLecturers(SearchLecturerValueObject searchObj) {
		return getSearchQuery(searchObj).getResultList().size();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Lecturer> searchLecturers(SearchLecturerValueObject searchObj, int page, int pageSize) {
		return (List<Lecturer>) getSearchQuery(searchObj).setFirstResult((page - 1) * pageSize)
				.setMaxResults(pageSize).getResultList();
	}
	
	private Query getSearchQuery(SearchLecturerValueObject searchObj) {
		EntityManager em = null;
		try {
			em = EMF.get().createEntityManager();
			CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
			CriteriaQuery<Lecturer> criteriaQuery = criteriaBuilder.createQuery(Lecturer.class);
			Root<Lecturer> root = criteriaQuery.from(Lecturer.class);
			List<Predicate> predicates = new ArrayList<Predicate>();
			
			if (searchObj.getLecturerId() != null && searchObj.getLecturerId().length() > 0) {
				Expression<String> lecturerIdExpression = root.get("lecturerId");
				predicates.add(criteriaBuilder.equal(lecturerIdExpression, searchObj.getLecturerId()));
			}
			
			if (searchObj.getLecturerName() != null && searchObj.getLecturerName().length() > 0) {
				Expression<String> lecturerNameExpression = root.get("lecturerName");
				predicates.add(criteriaBuilder.equal(lecturerNameExpression, searchObj.getLecturerName()));
			}
			
			if (searchObj.getLecturerEmail() != null && searchObj.getLecturerEmail().length() > 0) {
				Expression<String> lecturerEmailExpression = root.get("lecturerEmail");
				predicates.add(criteriaBuilder.equal(lecturerEmailExpression, searchObj.getLecturerEmail()));
			}
			
			if (searchObj.getLecturerDepartment() != null && searchObj.getLecturerDepartment().length() > 0) {
				Expression<String> lecturerDepartmentExpression = root.get("lecturerDepartment");
				predicates.add(criteriaBuilder.equal(lecturerDepartmentExpression, searchObj.getLecturerDepartment()));
			}
			
			if (predicates.isEmpty()) {
				criteriaQuery.select(root);
			} else {
				criteriaQuery.select(root).where(predicates.toArray(new Predicate[predicates.size()]));
			}
			
			TypedQuery<Lecturer> query = em.createQuery(criteriaQuery);
			return query;
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new AppSystemException(ex);
		}
	}
}
